#ifndef _TEXT_H_
#define _TEXT_H_

#include "./debugger.h"

#define I_O_SAFE_COMPILATION

#define ALLOW_FILE_FEEDBACK

//! Text conversions
/*!
	Conversions for manipulating string's, converting to numerical types (or from)
*/
namespace Text{
	using namespace Debug;

	//inline string operator + ( const string &str1, const string &str2 ){
	//	return str1 + str2;
	//}

	//inline string& operator += ( string &str1, const string &str2 ){
	//	return str1 = str1 + str2;
	//}

	//inline string operator + ( const string &str1, const char str2[] ){
	//	return str1 + str2;
	//}

	//inline string& operator += ( string &str1, const char str2[] ){
	//	return str1 = str1 + str2;
	//}

	//inline string operator + ( const char str1[], const string &str2 ){
	//	return str1 + str2;
	//}

	/*! used for char[] */
	const unsigned short int MaxLargText = 1024;
	
	/*! conversion: integer to string */
	inline string itos(const int &num){
		char out[MaxLargText];
		
#ifdef I_O_SAFE_COMPILATION
		// sprintf_s returns the number of characters written
		sprintf_s( out, MaxLargText, "%d", num );
#else
		sprintf(out,"%d",num);
#endif
		
		return string(out);
	};
	
	/*! conversion: string to int */
	inline int stoi(const string &s){
		int out = 0;
		
#ifdef I_O_SAFE_COMPILATION
		sscanf_s( s.c_str(), "%d", &out, sizeof(out) );
#else
		sscanf(s.c_str(),"%d",&out);
#endif
		
		return out;
	};
	
	/*! concatenation: string and int */
	inline string operator + ( const string &str, const int &number ){
		return str + itos(number);
	};
	/*! comutative operator + */
	inline string operator + ( const int &number, const string &str ){
		return string("") + number + str;
	};
	/*! concatenation and attribution: string and int */
	inline string& operator += ( string& str, const int &number ){
		return str = str + number;
	};

	/*! conversion: double to string */
	inline string dtos(const double &num){
		char out[MaxLargText];
		
#ifdef I_O_SAFE_COMPILATION
		sprintf_s( out, MaxLargText, "%0.4lf", num );
#else
		sprintf(out,"%0.4lf",num);
#endif
		
		return string(out);
	};
	
	/*! conversion: string to double */
	inline double stod(const string &s){
		double out = 0.0;
		
#ifdef I_O_SAFE_COMPILATION
		sscanf_s( s.c_str(), "%lf", &out, sizeof(out) );
#else
		sscanf(s.c_str(),"%lf",&out);
#endif
		
		return out;
	};

	/*! concatenation: string and double */
	inline string operator + ( const string &str, const double &number ){
		return str + dtos(number);
	};
	/*! comutative operator + */
	inline string operator + ( const double &number, const string &str ){
		return string("") + number + str;
	};
	/*! concatenation and attribution: string and double */
	inline string& operator += ( string& str, const double &number ){
		return str = str + number;
	};

	/*! file manipulation */
	inline bool file_open( const string &file_name, FILE *&ptr_file, const string &option_mode = "w+" ){
#ifdef I_O_SAFE_COMPILATION
		// fopen_s returns 0 if OK
		return fopen_s( &ptr_file, file_name.c_str(), option_mode.c_str() ) == 0;
#else
		return ( *ptr_file = fopen( file_name.c_str(), option_mode.c_str() ) ) != NULL;
#endif
	};

	inline bool file_close( FILE *ptr_file ){
		// fclose returns 0 if OK
		if( fclose( ptr_file ) != 0 )
			return false;
		ptr_file = NULL;
		return true;
	};

	inline bool file_print( const string &text, FILE *parq = stdout ){
#ifdef I_O_SAFE_COMPILATION
		return fprintf_s( parq, "%s", text.c_str() ) >= 1;
#else
		return fprintf( parq, "%s", text.c_str() ) >= 1;
#endif
	};

	inline bool file_scan( string &text, FILE *parq = stdin ){
		char out[MaxLargText];
		
#ifdef I_O_SAFE_COMPILATION
		if( fscanf_s( parq, "%s", out, MaxLargText ) < 1 )
			return false;
#else
		if( fscanf(parq,"%s",out) < 1 )
			return false;
#endif
		text = out;
		return true;
	};

	/*! function for feedback: scan */
	inline void fb_scan( const string &obj_name ){
		cout << "Scanning: " << obj_name << endl;
	};

	/*! function for feedback: print */
	inline void fb_print( const string &obj_name ){
		cout << "Printing: " << obj_name << endl;
	};

	inline bool operator == ( const string &left, const string &right ) {
		return left.compare(right) == 0;
	};

	inline bool operator != ( const string &left, const string &right ) {
		return left.compare(right) != 0;
	};

	//! Readable Class
	/*!
		Allows the guarantee of readability to son classes
	*/
	class Readable{
	public:
		//! Destructor
		virtual ~Readable() {};
		
		// --- Readable ---

		/*! 
			Reads a value for the object from an opened file.
			
			\param parq Pointer to the opened file.
			\return boolean indicating failure or success of the operation
		*/
		virtual bool fileScan( FILE *parq = stdin ) = 0;
		/*! 
			Opens a file with the given address.
			Then, reads the value of the object from the opened file.
			Finally, it closes the file.
			
			\param address Name of the file to be opened.
			\return boolean indicating failure or success of the operation
			
			\sa bool Readable::filePrint( FILE *parq = stdin )const
		*/
		bool fscan( const string &address );
		
	};

	/*! Allows writing with less characters */
	typedef Readable Rdb;

	//! Writeable Class
	/*!
		Allows the guarantee of writeability to son classes
	*/
	class Writeable{
	public:
		//! Destructor
		virtual ~Writeable() {};
		
		// --- Writeable ---

		/*! 
			Writes the value of the object in an opened file.
			
			\param parq Pointer to the opened file.
			\return boolean indicating failure or success of the operation
			
			\sa string Writeable::toS()const
		*/
		virtual bool filePrint( FILE *parq = stdin ) const = 0;
		/*! 
			Opens a file with the given address.
			Then, writes the value of the object into the opened file.
			Finally, it closes the file.
			
			\param address Name of the file to be opened.
			\return boolean indicating failure or success of the operation
			
			\sa bool Writeable::filePrint( FILE *parq = stdin )const
		*/
		bool fprint( const string &address ) const ;
		/*! 
			Method for converting the object in a string representation
			
			\return a string that represents the object.
		*/
		virtual string toS() const = 0;

		inline friend string operator + ( const Writeable &wtb, const string &str ) {
			return wtb.toS() + str;
		};

		inline friend string operator + ( const string &str, const Writeable &wtb ) {
			return str + wtb.toS();
		};

		inline friend string& operator += ( string &str, const Writeable &wtb ) {
			return str += wtb.toS();
		};

		inline friend string operator + ( const Writeable &wtb, const char str[] ) {
			return wtb.toS() + str;
		};

		inline friend string operator + ( const char str[], const Writeable &wtb ) {
			return str + wtb.toS();
		};

		/*! 
			ostream printing (default)
		
			\param file ostream where the value should be printed
			\param obj the object to be printed
			\return modified ostream
		
			\code
			Writeable obj;
			cout << obj << endl; // prints the object in the screen
			\endcode
		*/
		inline friend ostream& operator << (ostream &file, const Writeable &obj) {
			return file << obj.toS();
		};
	
	};

	/*! Allows writing with less characters */
	typedef Writeable Wtb;

};

#endif
